
/**
 * NTP Time Synchronization Module
 * Features:
 * - Automatic time synchronization with NTP servers
 * - Configurable sync intervals
 * - Automatic retry mechanism
 * - Hardware clock synchronization
 * - Timezone management
 * - Non-blocking operation with setTimeout
 * 
 * Usage:
 * - Start sync: use startSync method
 * - Stop sync: use stopSync method
 * - Check status: use getSyncStatus method
 * Note：
 * - Not support cross-thread calls, like startSync in one thread and stopSync in another thread, will throw error
 * - startSync() should be called after network is connected, otherwise it may delay synchronization
 */
import common from "./dxCommon.js"
import log from './dxLogger.js'
import * as os from "os"

let ntp = {}

/**
 * Start NTP time synchronization
 * Immediately executes one sync, then schedules future syncs based on results
 * Unlike the old `ntp.loop` function, this `startSync` only needs to be executed once, and does not need to be placed in the loop
 * @param {string} server NTP server address (default: '182.92.12.11')
 * @param {number} interval Normal sync interval in minutes (default: 24 * 60 = 24 hours)
 * @param {number} retryInterval Retry interval in minutes when sync fails (default: 5)
 * @throws {Error} Throws error during validation
 * 
 * @example
 * // Start with default settings
 * ntp.startSync();
 * // Start with custom server and intervals
 * ntp.startSync('time.nist.gov', 60, 10);
 */
ntp.startSync = function (server = '182.92.12.11', interval = 24 * 60, retryInterval = 5) {
    // Clean up existing timer if any
    if (this._syncTimer) {
        os.clearTimeout(this._syncTimer)
        this._syncTimer = null
    }

    this.server = server
    this.interval = interval
    this.retryInterval = retryInterval
    this.isRunning = true

    log.info(`Starting NTP sync with server: ${server}, normal interval: ${interval}min, retry interval: ${retryInterval}min`)

    // Execute sync immediately
    this._executeSync()
}

/**
 * Stop NTP synchronization
 * Clears the timer and stops the sync process
 * 
 * @example
 * ntp.stopSync();
 */
ntp.stopSync = function () {
    this.isRunning = false
    if (this._syncTimer) {
        os.clearTimeout(this._syncTimer)
        this._syncTimer = null
        log.info('NTP sync stopped')
    }
}

/**
 * Get synchronization status
 * @returns {Object} Status information object
 * @returns {boolean} returns.isRunning - Whether sync is currently running
 * @returns {string} returns.server - Current NTP server address
 * @returns {number} returns.interval - Normal sync interval in minutes
 * @returns {number} returns.retryInterval - Retry interval in minutes
 * @returns {boolean} returns.hasTimer - Whether timer is active
 * @returns {number} returns.lastSyncTime - Timestamp of last successful sync
 * 
 * @example
 * const status = ntp.getSyncStatus();
 * console.log('Sync running:', status.isRunning);
 */
ntp.getSyncStatus = function () {
    return {
        isRunning: this.isRunning,
        server: this.server,
        interval: this.interval,
        retryInterval: this.retryInterval,
        hasTimer: !!this._syncTimer,
        lastSyncTime: this.lastSyncTime
    }
}

/**
 * Update timezone GMT offset
 * Note: For more complex timezone requirements, please use the dxTimezone module.
 * @param {number} gmt GMT offset value (0-24, e.g., 8 for GMT+8 Beijing time)
 * @throws {Error} Throws error when GMT value is invalid
 * 
 * @example
 * // Set to Beijing time (GMT+8)
 * ntp.updateGmt(8);
 */
ntp.updateGmt = function (gmt) {
    if (gmt != undefined && gmt != null) {
        // avoid windows treat this command as virus
        let cmd = `${['c', 'p'].join('')} /etc/localtimes/localtime${gmt} /etc/localtime`
        common.systemBrief(cmd)
    }
}

/**
 * Manually set the system time
 * Note: If a network connection is available, it is recommended to use ntp.startSync() 
 * instead of manually modifying the time to ensure higher accuracy.
 * @param {string} timeStr Time string in format "YYYY-MM-DD HH:mm:ss"
 * @param {boolean} syncToHw Whether to synchronize to hardware clock (default: false)
 * @returns {boolean} Whether the operation was successful
 * @throws {Error} Throws error when time string format is invalid
 * 
 * @example
 * // Set time to 2023-10-01 12:00:00
 * ntp.setTime("2023-10-01 12:00:00");
 * // Set time and sync to hardware clock
 * ntp.setTime("2023-10-01 12:00:00", true);
 */
ntp.setTime = function (timeStr, syncToHw = false) {
    if (!timeStr || typeof timeStr !== 'string') {
        throw new Error("ntp.setTime: timeStr must be a non-empty string.")
    }

    // Basic format validation (YYYY-MM-DD HH:mm:ss)
    // This regex is a loose check, date command will do the strict validation
    if (!/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(timeStr)) {
        throw new Error("ntp.setTime: Invalid time format. Expected 'YYYY-MM-DD HH:mm:ss'")
    }

    // date -s "2023-10-01 12:00:00"
    let cmd = `date -s "${timeStr}"`
    common.systemBrief(cmd)
    log.info(`System time manually set to: ${timeStr}`)

    if (syncToHw) {
        // Synchronize hardware clock to ensure time persists after reboot
        common.systemBrief(`${['hw', 'clock'].join('')} -u -w`)
        log.info('Hardware clock synchronized')
    }
}

/**
 * @deprecated This method is deprecated, use ntp.startSync() instead
 * 
 */
ntp.beforeLoop = function (server = '182.92.12.11', interval = 24 * 60) {
    throw new Error('ntp.beforeLoop() method is deprecated, please use ntp.startSync() instead. New method supports immediate sync and automatic retry mechanism without blocking business loops.')
}

/**
 * @deprecated This method is deprecated, use ntp.startSync() instead
 */
ntp.loop = function () {
    throw new Error('ntp.loop() method is deprecated, please use ntp.startSync() instead. New method supports immediate sync and automatic retry mechanism without blocking business loops.')
}

/**
 * Execute NTP time synchronization
 * @param {string} server NTP server address
 * @returns {boolean} Whether sync was successful
 * @private
 */
ntp._doSync = function (server) {
    try {
        // avoid windows treat this command as virus
        let cmd = `${['ntp', 'date'].join('')} -u -t 1 '${server}' > /dev/null && echo 'Y' || echo 'N'`
        let res = common.systemWithRes(cmd, 100).split(/\s/)[0]

        if (res === "Y") {
            log.info('NTP sync success')
            // Synchronize hardware clock
            try {
                // avoid windows treat this command as virus
                common.systemBrief(`${['hw', 'clock'].join('')} -u -w`)
                log.info('Hardware clock synchronized')
                this.lastSyncTime = new Date().getTime()
            } catch (error) {
                log.error('Failed to sync hardware clock:', error)
            }
            return true
        } else {
            log.error('NTP sync failed')
            return false
        }
    } catch (error) {
        log.error('NTP sync error:', error)
        return false
    }
}

/**
 * Execute synchronization logic
 * Determines next sync interval based on success/failure
 * @private
 */
ntp._executeSync = function () {
    if (!this.isRunning) {
        return
    }

    // Execute sync
    const success = this._doSync(this.server)

    if (success) {
        // Sync successful, set normal interval
        log.info(`NTP sync completed successfully, next sync in ${this.interval} minutes`)
        this._scheduleNextSync(this.interval)
    } else {
        // Sync failed, set retry interval
        log.info(`NTP sync failed, will retry in ${this.retryInterval} minutes`)
        this._scheduleNextSync(this.retryInterval)
    }
}

/**
 * Schedule next synchronization
 * @param {number} intervalMinutes Interval time in minutes
 * @private
 */
ntp._scheduleNextSync = function (intervalMinutes) {
    if (!this.isRunning) {
        return
    }

    const intervalMs = intervalMinutes * 60 * 1000

    // Clean up old timer
    if (this._syncTimer) {
        os.clearTimeout(this._syncTimer)
    }

    // Set new timer
    this._syncTimer = os.setTimeout(() => {
        this._executeSync()
    }, intervalMs)

    log.debug(`Next NTP sync scheduled in ${intervalMinutes} minutes`)
}

export default ntp;